package com.duoduo.admin.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;

import com.duoduo.admin.api.dto.SysPostDTO;
import com.duoduo.admin.api.dto.SysRoleDTO;
import com.duoduo.admin.api.dto.SysUserDTO;
import com.duoduo.admin.api.dto.UserDTO;
import com.duoduo.admin.api.dto.UserInfo;
import com.duoduo.admin.api.util.ParamResolver;
import com.duoduo.admin.api.vo.UserExcelVO;
import com.duoduo.admin.entity.SysDept;
import com.duoduo.admin.entity.SysMenu;
import com.duoduo.admin.entity.SysPost;
import com.duoduo.admin.entity.SysRole;
import com.duoduo.admin.entity.SysUser;
import com.duoduo.admin.entity.SysUserPost;
import com.duoduo.admin.entity.SysUserRole;
import com.duoduo.admin.repository.SysDeptRepository;
import com.duoduo.admin.repository.SysPostRepository;
import com.duoduo.admin.repository.SysRoleRepository;
import com.duoduo.admin.repository.SysUserRepository;
import com.duoduo.admin.repository.UserPostRepository;
import com.duoduo.admin.repository.UserRoleRepository;
import com.duoduo.common.core.constant.CacheConstants;
import com.duoduo.common.core.constant.CommonConstants;
import com.duoduo.common.core.constant.enums.MenuTypeEnum;
import com.duoduo.common.core.exception.DuoException;
import com.duoduo.common.core.exception.ErrorCode;
import com.duoduo.common.core.exception.ErrorCodes;
import com.duoduo.common.core.util.MsgUtils;
import com.duoduo.common.core.util.R;
import com.duoduo.common.jpa.service.CommonService;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.validation.BeanValidationResult.ErrorMessage;
import cn.hutool.json.JSONUtil;
import jakarta.persistence.criteria.Predicate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class SysUserService extends CommonService<SysUserDTO, SysUser, Long>{

	private final SysUserRepository sysUserRepository;
	private final SysRoleRepository sysRoleRepository;
	private final SysDeptRepository sysDeptRepository;
	private final UserRoleRepository userRoleRepository;
	private final UserPostRepository userPostRepository;
	private final SysPostRepository sysPostRepository;
	private final SysMenuService sysMenuService;
	private final AppService appService;
	private final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
	
	@Override
	public Specification<SysUser> getWhere(final SysUserDTO request) {
		return (root, query, cb) -> {
			final List<Predicate> predicate = new ArrayList<Predicate>();
			if(request==null) {
				final Predicate[] pre = new Predicate[predicate.size()];
				return query.where(predicate.toArray(pre)).getRestriction();
			}
			if(request.getDeptId()!= null) {
				predicate.add(cb.equal(root.get("deptId"), request.getDeptId()));	
			}
			if(StringUtils.hasText(request.getPhone())) {
				 predicate.add(cb.equal(root.get("phone"), request.getPhone()));	
			}
			
			final Predicate[] pre = new Predicate[predicate.size()];
			return query.where(predicate.toArray(pre)).getRestriction();
		};
	}
	
	@Transactional(rollbackFor = Exception.class)
	public void saveUser(UserDTO userDto) {
		SysUser sysUser = new SysUser();
		BeanUtils.copyProperties(userDto, sysUser);
		sysUser.setDelFlag(CommonConstants.STATUS_NORMAL);
		sysUser.setPassword(passwordEncoder.encode(userDto.getPassword()));
		sysUserRepository.save(sysUser);
		userDto.getRole().stream().map(roleId -> {
			SysUserRole userRole = new SysUserRole();
			userRole.setUserId(sysUser.getUserId());
			userRole.setRoleId(roleId);
			return userRole;
		}).forEach(e ->{
			userRoleRepository.save(e);
		});
		// 保存用户岗位信息
		Optional.ofNullable(userDto.getPost()).ifPresent(posts -> {
			posts.stream().map(postId -> {
				SysUserPost userPost = new SysUserPost();
				userPost.setUserId(sysUser.getUserId());
				userPost.setPostId(postId);
				return userPost;
			}).forEach(p ->{
				userPostRepository.save(p);
			});
		});
	}
	public UserInfo getUserInfo(SysUser sysUser) {
		UserInfo userInfo = new UserInfo();
		SysUserDTO sysUserDTO = new SysUserDTO();
		BeanUtils.copyProperties(userInfo, sysUserDTO);
		userInfo.setSysUser(sysUserDTO);
		// 设置角色列表
		sysUser = sysUserRepository.findById(sysUser.getId()).orElseThrow();
		List<SysRoleDTO> roleList = sysUser.getAuthorities().stream().map(p ->{
			SysRoleDTO sysRoleDTO = new SysRoleDTO();
			BeanUtils.copyProperties(p, sysRoleDTO);
			return sysRoleDTO;
		}).collect(Collectors.toList());
	
		userInfo.setRoleList(roleList);
		
		// 设置角色列表 （ID）
		List<Long> roleIds = roleList.stream().map(SysRoleDTO::getId).collect(Collectors.toList());
		userInfo.setRoles(ArrayUtil.toArray(roleIds, Long.class));
		// 设置岗位列表
		List<SysPostDTO> postList = sysUser.getPosts().stream().map(p ->{
			SysPostDTO sysPostDTO = new SysPostDTO();
			BeanUtils.copyProperties(p, sysPostDTO);
			return sysPostDTO;
		}).collect(Collectors.toList());
		userInfo.setPostList(postList);
		// 设置权限列表（menu.permission）
		Set<String> permissions = roleIds.stream().map(sysMenuService::findMenuByRoleId).flatMap(Collection<SysMenu>::stream)
				//.filter(m -> MenuTypeEnum.BUTTON.getType().equals(m.getType()))
				.map(SysMenu::getPermissions)
				.filter(StrUtil::isNotBlank).collect(Collectors.toSet());
		userInfo.setPermissions(ArrayUtil.toArray(permissions, String.class));

		return userInfo;
	}
	/**
	 * 删除用户
	 * @param sysUser 用户
	 * @return Boolean
	 */
	@CacheEvict(value = CacheConstants.USER_DETAILS, key = "#sysUser.username")
	public void removeUserById(SysUser sysUser) {
		sysUser = sysUserRepository.findById(sysUser.getId()).orElseThrow();
		sysUserRepository.delete(sysUser);
		sysUser.getPosts();
		// 删除用户职位关系
		List<SysUserPost> posts = userPostRepository.findByUserId(sysUser.getUserId());
		if(!posts.isEmpty()) {
			userPostRepository.deleteAll(posts);	
		}
		// 删除用户菜单关系
		 List<SysUserRole> roles = userRoleRepository.findByUserId(sysUser.getUserId());
		if(!roles.isEmpty()) {
			userRoleRepository.deleteAll(roles);	
		}
	}
	
	@CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username")
	public R<Void> updateUserInfo(UserDTO userDto) {
		SysUser sysUser = sysUserRepository.findByUsername(userDto.getUsername()).orElseThrow();
		// 判断手机号是否修改,更新手机号校验验证码
		if (!StrUtil.equals(sysUser.getPhone(), userDto.getPhone())) {
			
			if (!appService.check(userDto.getPhone(), userDto.getCode())) {
				return R.failed(ErrorCode.SYS_APP_SMS_ERROR);
			}
		}

		// 修改密码逻辑
		SysUser newSysUser = new SysUser();
		if (StrUtil.isNotBlank(userDto.getNewpassword1())) {
			Assert.isTrue(passwordEncoder.matches(userDto.getPassword(), sysUser.getPassword()),
					MsgUtils.getMessage(ErrorCodes.SYS_USER_UPDATE_PASSWORDERROR));
			sysUser.setPassword(passwordEncoder.encode(userDto.getNewpassword1()));
		}
		newSysUser.setPhone(userDto.getPhone());
		newSysUser.setUserId(sysUser.getUserId());
		newSysUser.setAvatar(userDto.getAvatar());
		sysUserRepository.save(newSysUser);
		return R.ok();
	}
	
	
	@Transactional(rollbackFor = Exception.class)
	@CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username")
	public R<Boolean> updateUser(UserDTO userDto) {
		SysUser sysUser = new SysUser();
		BeanUtils.copyProperties(userDto, sysUser);

		if (StrUtil.isNotBlank(userDto.getPassword())) {
			sysUser.setPassword(passwordEncoder.encode(userDto.getPassword()));
		}
		sysUserRepository.save(sysUser);
		// 删除用户菜单关系
	    List<SysUserRole> roles = userRoleRepository.findByUserId(sysUser.getUserId());
				if(!roles.isEmpty()) {
					userRoleRepository.deleteAll(roles);	
				}
		userDto.getRole().forEach(roleId -> {
			SysUserRole userRole = new SysUserRole();
			userRole.setUserId(sysUser.getUserId());
			userRole.setRoleId(roleId);
			userRoleRepository.save(userRole);
		});
		// 删除用户职位关系
		List<SysUserPost> posts = userPostRepository.findByUserId(sysUser.getUserId());
		if(!posts.isEmpty()) {
			userPostRepository.deleteAll(posts);	
		}
		userDto.getPost().forEach(postId -> {
			SysUserPost userPost = new SysUserPost();
			userPost.setUserId(sysUser.getUserId());
			userPost.setPostId(postId);
			userPostRepository.save(userPost);
		});
		return R.ok();
	}
	
	public UserInfo info(String username) {
		UserInfo userInfo = new UserInfo();
		SysUser sysUser = sysUserRepository.findByUsername(username).orElseThrow(() ->{
			return new DuoException(ErrorCode.NOT_FIND);
		});
		SysUserDTO sysUserDTO = new SysUserDTO();
		BeanUtils.copyProperties(sysUser, sysUserDTO);
		userInfo.setSysUser(sysUserDTO);
		List<String> list = sysUser.getAuthorities().stream().flatMap(p ->p.getPermissions().stream().filter(f -> StringUtils.hasText(f.getPermissions())).map(r -> r.getPermissions())).collect(Collectors.toList());
		userInfo.setPermissions(ArrayUtil.toArray(list, String.class));
		List<Long> roles = sysUser.getAuthorities().stream().map(p -> p.getId()).toList();
		userInfo.setRoleList(sysUser.getAuthorities().stream().map(p ->{
			SysRoleDTO sysRoleDTO = new SysRoleDTO();
			BeanUtils.copyProperties(p, sysRoleDTO);
			return sysRoleDTO;
		}).toList());
		userInfo.setPostList(sysUser.getPosts().stream().map(p ->{
			SysPostDTO sysPostDTO = new SysPostDTO();
			BeanUtils.copyProperties(p, sysPostDTO);
			return sysPostDTO;
		}).toList());
		userInfo.setPosts(ArrayUtil.toArray(sysUser.getPosts().stream().map(p ->p.getId()).toList(),Long.class));
		userInfo.setRoles(ArrayUtil.toArray(roles, Long.class));
		return userInfo;
	}
	
	/**
	 * 查询上级部门的用户信息
	 * @param username 用户名
	 * @return R
	 */
	public List<SysUser> listAncestorUsersByUsername(String username) {
		SysUser sysUser = sysUserRepository.findByUsername(username).orElseThrow();
		 SysPost sysDept = sysPostRepository.findById(sysUser.getDeptId()).orElseThrow();
		 
		Long parentId = sysDept.getParent().getId();
		return sysUserRepository.findByDeptId(parentId);
	}
	/**
	 * 查询全部的用户
	 * @param userDTO 查询条件
	 * @return list
	 */
	public List<UserExcelVO> listUser(SysUserDTO userDTO) {
		List<SysUser> voList = sysUserRepository.findAll(this.getWhere(userDTO));
		// 转换成execl 对象输出
		List<UserExcelVO> userExcelVOList = voList.stream().map(userVO -> {
			UserExcelVO excelVO = new UserExcelVO();
			BeanUtils.copyProperties(userVO, excelVO);
			String roleNameList = userVO.getAuthorities().stream().map(SysRole::getRoleName)
					.collect(Collectors.joining(StrUtil.COMMA));
			excelVO.setRoleNameList(roleNameList);
			String postNameList = userVO.getPosts().stream().map(SysPost::getPostName)
					.collect(Collectors.joining(StrUtil.COMMA));
			excelVO.setPostNameList(postNameList);
			return excelVO;
		}).collect(Collectors.toList());
		return userExcelVOList;
	}
	
	/**
	 * excel 导入用户, 插入正确的 错误的提示行号
	 * @param excelVOList excel 列表数据
	 * @param bindingResult 错误数据
	 * @return ok fail
	 */
	public R importUser(List<UserExcelVO> excelVOList, BindingResult bindingResult) {
		// 通用校验获取失败的数据
		List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();

		// 个性化校验逻辑
		List<SysUser> userList = sysUserRepository.findAll();
		List<SysDept> deptList = sysDeptRepository.findAll();
		List<SysRole> roleList = sysRoleRepository.findAll();
		List<SysPost> postList = sysPostRepository.findAll();

		// 执行数据插入操作 组装 UserDto
		for (UserExcelVO excel : excelVOList) {
			Set<String> errorMsg = new HashSet<>();
			// 校验用户名是否存在
			boolean exsitUserName = userList.stream()
					.anyMatch(sysUser -> excel.getUsername().equals(sysUser.getUsername()));

			if (exsitUserName) {
				errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, excel.getUsername()));
			}

			// 判断输入的部门名称列表是否合法
			Optional<SysDept> deptOptional = deptList.stream()
					.filter(dept -> excel.getDeptName().equals(dept.getName())).findFirst();
			if (!deptOptional.isPresent()) {
				errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_DEPT_DEPTNAME_INEXISTENCE, excel.getDeptName()));
			}

			// 判断输入的角色名称列表是否合法
			List<String> roleNameList = StrUtil.split(excel.getRoleNameList(), StrUtil.COMMA);
			List<SysRole> roleCollList = roleList.stream()
					.filter(role -> roleNameList.stream().anyMatch(name -> role.getRoleName().equals(name)))
					.collect(Collectors.toList());

			if (roleCollList.size() != roleNameList.size()) {
				errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_ROLE_ROLENAME_INEXISTENCE, excel.getRoleNameList()));
			}

			// 判断输入的岗位名称列表是否合法
			List<String> postNameList = StrUtil.split(excel.getPostNameList(), StrUtil.COMMA);
			List<SysPost> postCollList = postList.stream()
					.filter(post -> postNameList.stream().anyMatch(name -> post.getPostName().equals(name)))
					.collect(Collectors.toList());

			if (postCollList.size() != postNameList.size()) {
				errorMsg.add(MsgUtils.getMessage(ErrorCodes.SYS_POST_POSTNAME_INEXISTENCE, excel.getPostNameList()));
			}

			// 数据合法情况
			if (CollUtil.isEmpty(errorMsg)) {
				insertExcelUser(excel, deptOptional, roleCollList, postCollList);
			}
			else {
				// 数据不合法情况
				errorMessageList.add(new ErrorMessage());
			}

		}

		if (CollUtil.isNotEmpty(errorMessageList)) {
			log.error(JSONUtil.toJsonStr(errorMessageList));
			return R.failed(ErrorCode.ERROR);
		}
		return R.ok();
	}

	
	public List<Long> listUserIdByDeptIds(Set<Long> deptIds) {
		return sysUserRepository.findByDeptIdIn(deptIds).stream().map(p ->p.getId()).toList();
	}

	/**
	 * 插入excel User
	 */
	private void insertExcelUser(UserExcelVO excel, Optional<SysDept> deptOptional, List<SysRole> roleCollList,
			List<SysPost> postCollList) {
		UserDTO userDTO = new UserDTO();
		userDTO.setUsername(excel.getUsername());
		userDTO.setPhone(excel.getPhone());
		// 批量导入初始密码为手机号
		userDTO.setPassword(userDTO.getPhone());
		// 根据部门名称查询部门ID
		userDTO.setDeptId(deptOptional.get().getId());
		// 根据角色名称查询角色ID
		List<Long> roleIdList = roleCollList.stream().map(SysRole::getId).collect(Collectors.toList());
		userDTO.setRole(roleIdList);
		List<Long> postIdList = postCollList.stream().map(SysPost::getId).collect(Collectors.toList());
		userDTO.setPost(postIdList);
		// 插入用户
		this.saveUser(userDTO);
	}

	/**
	 * 注册用户 赋予用户默认角色
	 * @param userDto 用户信息
	 * @return success/false
	 */
	public R<Void> registerUser(UserDTO userDto) {
		// 校验验证码
		if (!appService.check(userDto.getPhone(), userDto.getCode())) {
			return R.failed(ErrorCode.SYS_APP_SMS_ERROR);
		}

		// 判断用户名是否存在
		 Optional<SysUser> optUser = sysUserRepository.findByUsername(userDto.getUsername());
		if (optUser.isPresent()) {
			return R.failed(ErrorCode.SYS_USER_USERNAME_EXISTING);
		}

		// 获取默认角色编码
		String defaultRole = ParamResolver.getStr("USER_DEFAULT_ROLE");
		// 默认角色
		SysRole sysRole = sysRoleRepository.findByRoleCode(defaultRole).orElseThrow(() ->{
			return new DuoException(ErrorCode.SYS_PARAM_CONFIG_ERROR);
		});

		userDto.setRole(Collections.singletonList(sysRole.getId()));
		saveUser(userDto);
		return R.ok();
	}
	
	public UserInfo infoByMobile(String phone) {
		SysUser sysUser = sysUserRepository.findByUsername(phone).orElseThrow();
		return getUserInfo(sysUser);
	}


}
